using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;

namespace Framework
{
    // [CreateAssetMenu]
    public class ShaderConfig : ScriptableObject
    {
        private const string ConfigFile = FrameworkSetting.SETTING_FOLDER + "ShaderConfig.asset";
        public List<string> Ignores = new List<string>();
        public List<ShaderItemData> Shaders = new List<ShaderItemData>();
        private static ShaderConfig instance = null;
        private Dictionary<string, ShaderItemData> _shaderMap = new Dictionary<string, ShaderItemData>();
        public static ShaderConfig GetInstance()
        {
            if (instance != null)
            {
                return instance;
            }
            if (File.Exists(ConfigFile))
            {
                instance = AssetDatabase.LoadAssetAtPath<ShaderConfig>(ConfigFile);
            }
            else
            {
                FrameworkSetting.CreateFolder();
                instance = CreateInstance<ShaderConfig>();
                AssetDatabase.CreateAsset(instance, ConfigFile);
            }
            if (instance != null)
            {
                instance.init();
            }
            return instance;
        }
        private void init()
        {
            if (Shaders == null)
            {
                Shaders = new List<ShaderItemData>();
            }
            if (_shaderMap == null)
            {
                _shaderMap = new Dictionary<string, ShaderItemData>();
            }
            if (Ignores == null || Ignores.Count < 1)
            {
                Ignores = new List<string>() { "STEREO_CUBEMAP_RENDER_ON", "STEREO_INSTANCING_ON", "STEREO_MULTIVIEW_ON", "UNITY_SINGLE_PASS_STEREO" };
            }
            var len = Shaders.Count;
            while (len-- > 0)
            {
                var item = Shaders[len];
                if (string.IsNullOrEmpty(item.Name))
                {
                    Shaders.RemoveAt(len);
                    continue;
                }
                if (_shaderMap.ContainsKey(item.Name)) continue;
                _shaderMap.Add(item.Name, item);
            }

        }
        public void Update()
        {
            if (Ignores != null && Ignores.Count < 1)
            {
                Ignores.Add("STEREO_CUBEMAP_RENDER_ON");
                Ignores.Add("STEREO_INSTANCING_ON");
                Ignores.Add("STEREO_MULTIVIEW_ON");
                Ignores.Add("UNITY_SINGLE_PASS_STEREO");
            }
            _shaderMap.Clear();
            var len = Shaders.Count;
            while (len-- > 0)
            {
                var item = Shaders[len];
                item.IgnoreKeyword(Ignores);
                if (_shaderMap.ContainsKey(item.Name))
                {
                    Shaders.RemoveAt(len);
                    continue;
                }
                var shader = item.shader;
                if (shader != null)
                {
                    var keywords = getKeywords(shader);
                    item.Disabled = keywords.Count < 2;
                }
                _shaderMap.Add(item.Name, item);
            }
            Shaders.Sort(sortByName);
        }
        public void AddShader(Shader value)
        {
            var keywords = getKeywords(value);
            if (keywords.Count < 2) return;
            if (!_shaderMap.TryGetValue(value.name, out ShaderItemData item))
            {
                item = new ShaderItemData();
                Shaders.Add(item);
                _shaderMap.Add(value.name, item);
            }
            item.Set(value);
        }
        public bool IsEnabledKeyword(string shaderName, string keyword)
        {
            if (_shaderMap.TryGetValue(shaderName, out ShaderItemData item))
            {
                return item.IsEnabledKeyword(keyword);
            }
            return true;
        }
        public bool IsEnabledKeyword(Shader shader, ShaderKeyword keyword)
        {
            string keywordName = null;
#if UNITY_2021_1_OR_NEWER
            keywordName = keyword.name;
#else
            keywordName = ShaderKeyword.GetKeywordName(shader, keyword);
#endif

            return IsEnabledKeyword(shader.name, keywordName);
        }
        public void Save()
        {
            if (instance != null)
            {
                EditorUtility.SetDirty(instance);
                AssetDatabase.SaveAssets();
                instance = null;
            }
        }
        public string GetKeywordName(Shader shader, ShaderKeyword keyword)
        {


#if UNITY_2021_1_OR_NEWER
            return shader.name;
#else
             return ShaderKeyword.GetKeywordName(shader, keyword);
#endif
        }
        private List<string> getKeywords(Shader value)
        {
            var list = new List<string>();
#if UNITY_2021_1_OR_NEWER
            var names = value.keywordSpace.keywords;
            var len = names.Length;
            for (int i = 0; i < len; i++)
            {
                var name = names[i].name;
                if (Ignores.Contains(name)) continue;
                list.Add(name);
            }
#endif
            return list;
        }
        private int sortByName(ShaderItemData a, ShaderItemData b)
        {
            return a.Name.CompareTo(b.Name);
        }
    }
    [System.Serializable]
    public class ShaderItemData
    {
        public Shader shader { get; private set; }
        [System.NonSerialized]
        public bool Disabled;
        [HideInInspector]
        public string Name;
        public List<string> EnableKeyword = new List<string>();
        public void Set(Shader shader)
        {
            bool add = string.IsNullOrEmpty(this.Name);
            this.shader = shader;
            this.Name = shader.name;
            if (add)
            {
                EnableKeyword.Clear();
#if UNITY_2021_1_OR_NEWER
                var names = shader.keywordSpace.keywords;
                var len = names.Length;
                for (int i = 0; i < len; i++)
                {
                    var name = names[i].name;
                    EnableKeyword.Add(name);
                }
#endif
            }
        }
        public bool IsEnabledKeyword(string keyword)
        {
            if (Disabled) return true;
            return EnableKeyword.Contains(keyword);
        }
        public void IgnoreKeyword(List<string> list)
        {
            var len = EnableKeyword.Count;
            while (len-- > 0)
            {
                if (list.Contains(EnableKeyword[len]))
                {
                    EnableKeyword.RemoveAt(len);
                }
            }
        }

    }
}

/*
using System.Collections.Generic;
using Framework;
using UnityEditor.Build;
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Rendering;

public class CustomShaderProcess : IPreprocessShaders
{
    public int callbackOrder { get { return 0; } }
    public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
    {
        var config = ShaderConfig.GetInstance();
        if (config == null) return;
        var shaderName = shader.name;
        var len = data.Count;
        while (len-- > 0)
        {
            var keywordset = data[len].shaderKeywordSet;
            var list = keywordset.GetShaderKeywords();
            var c = list.Length;
            if (c < 1) continue;
            foreach (var item in list)
            {
                if (config.IsEnabledKeyword(shader, item))
                {
                    continue;
                }
                keywordset.Disable(item);
                // Debug.LogError("disable " + name);
                c--;
            }
            if (c < 1)
            {
                data.RemoveAt(len);
                // Debug.LogError("remove " + shaderName);
            }
        }
    }
}
*/